using System;
using System.Collections.Generic;
namespace Common
{
    public interface IReference
    {
        void Clear();
    }


    public class ReferencePool
    {
        /// <summary>
        /// 总引用池
        /// </summary>
        private static readonly Dictionary<Type, ReferenceCollection> s_ReferenceCollections =
            new Dictionary<Type, ReferenceCollection>();

        /// <summary>
        /// 是否启用严格检查
        /// </summary>
        private static bool m_EnableStrictCheck = false;

        /// <summary>
        /// 是否启用严格检查
        /// </summary>
        public static bool EnableStrictCheck
        {
            get => ReferencePool.m_EnableStrictCheck;
            set => ReferencePool.m_EnableStrictCheck = value;
        }

        /// <summary>
        /// 引用池数量
        /// </summary>
        public static int Count => ReferencePool.s_ReferenceCollections.Count;


        /// <summary>
        /// 获取所有引用池信息
        /// </summary>
        /// <returns></returns>
        public static ReferencePoolInfo[] GetAllReferencePoolInfos()
        {
            int index = 0;
            ReferencePoolInfo[] referencePoolInfos;
            lock (ReferencePool.s_ReferenceCollections)
            {
                referencePoolInfos = new ReferencePoolInfo[ReferencePool.s_ReferenceCollections.Count];
                foreach (var referenceCollection in ReferencePool.s_ReferenceCollections)
                {
                    referencePoolInfos[index++] = new ReferencePoolInfo(
                        referenceCollection.Key,
                        referenceCollection.Value.UnUsedReferenceCount,
                        referenceCollection.Value.UsingReferenceCount,
                        referenceCollection.Value.AccquireReferenceCount,
                        referenceCollection.Value.ReleaseReferenceCount,
                        referenceCollection.Value.AddReferenceCount,
                        referenceCollection.Value.RemoveReferenceCount
                    );
                }

                return referencePoolInfos;
            }
        }

        /// <summary>
        /// 清空所有引用池
        /// </summary>
        public static void ClearAll()
        {
            lock (ReferencePool.s_ReferenceCollections)
            {
                foreach (var referenceCollection in ReferencePool.s_ReferenceCollections)
                {
                    referenceCollection.Value.RemoveAll();
                }

                ReferencePool.s_ReferenceCollections.Clear();
            }
        }

        /// <summary>
        /// 检查类型是否合法
        /// </summary>
        /// <param name="referenceType"></param>
        /// <exception cref="Exception"></exception>
        private static void InternalCheckReferenceType(Type referenceType)
        {
            //  不是严格模式直接返回
            if (!ReferencePool.m_EnableStrictCheck)
                return;
            if (referenceType == null)
                throw new Exception($"Type is Invalid");
            //  该类不是类或者为抽象类，则抛出异常
            if (!referenceType.IsClass || referenceType.IsAbstract)
                throw new Exception($"Type is Invalid");
            //  该类不实现IReference接口，则抛出异常
            if (!typeof(IReference).IsAssignableFrom(referenceType))
                throw new Exception($"Type is Invalid");
        }
        
        /// <summary>
        /// 获取引用
        /// </summary>
        /// <param name="referenceType"></param>
        /// <returns></returns>
        public static IReference Acquire(Type referenceType)
        {
            ReferencePool.InternalCheckReferenceType(referenceType);
            return GetReferenceCollection(referenceType).Acquire();
        }

        public static T Acquire<T>() where T : class, IReference, new() => Acquire(typeof(T)) as T;


        /// <summary>
        /// 获取引用池
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        private static ReferenceCollection GetReferenceCollection(Type referenceType)
        {
            if(referenceType == null)
                throw new Exception($"Type is Invalid");
            ReferenceCollection referenceCollection;
            lock (ReferencePool.s_ReferenceCollections)
            {
                if (!ReferencePool.s_ReferenceCollections.TryGetValue(referenceType, out referenceCollection))
                {
                    referenceCollection = new ReferenceCollection(referenceType);
                    ReferencePool.s_ReferenceCollections.Add(referenceType, referenceCollection);
                }
            }
            return referenceCollection;
        }
        
        /// <summary>
        /// 释放引用
        /// </summary>
        /// <param name="reference"></param>
        /// <exception cref="Exception"></exception>
        public static void Release(IReference reference)
        {
            Type referenceType = reference != null ? reference.GetType() : throw new Exception("reference is null");
            ReferencePool.InternalCheckReferenceType(referenceType);
            ReferencePool.GetReferenceCollection(referenceType).Release(reference);
        }






















































        private sealed class ReferenceCollection
        {
            public int UnUsedReferenceCount => m_References.Count;
            public int UsingReferenceCount => m_UsingReferenceCount;

            public int AccquireReferenceCount => m_AccquireReferenceCount;

            public int ReleaseReferenceCount => m_ReleaseReferenceCount;

            public int AddReferenceCount => m_AddReferenceCount;

            public int RemoveReferenceCount => m_RemoveReferenceCount;

            /// <summary>
            /// 某一类型的对象池
            /// </summary>
            private readonly Queue<IReference> m_References;
            /// <summary>
            /// 引用类型
            /// </summary>
            private readonly Type m_ReferenceType;
            /// <summary>
            /// 正在使用的引用数量
            /// </summary>
            private int m_UsingReferenceCount;
            /// <summary>
            /// 总获取引用的数量
            /// </summary>
            private int m_AccquireReferenceCount;
            /// <summary>
            /// 释放的引用数量
            /// </summary>
            private int m_ReleaseReferenceCount;
            /// <summary>
            /// 增加的引用数量(即总共实例化的数量)
            /// </summary>
            private int m_AddReferenceCount;
            /// <summary>
            /// 移除的引用数量
            /// </summary>
            private int m_RemoveReferenceCount;
            
            
            public ReferenceCollection(Type referenceType)
            {
                this.m_References = new Queue<IReference>();
                this.m_ReferenceType = referenceType;
                this.m_UsingReferenceCount = 0;
                this.m_AccquireReferenceCount = 0;
                this.m_ReleaseReferenceCount = 0;
                this.m_AddReferenceCount = 0;
                this.m_RemoveReferenceCount = 0;
            }

            /// <summary>
            /// 获取引用
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <returns></returns>
            /// <exception cref="Exception"></exception>
            public T Acquire<T>() where T : class, IReference, new()
            {
                if(typeof(T) != this.m_ReferenceType)
                    throw new Exception($"Type is Invalid");
                ++this.m_AccquireReferenceCount;
                ++this.m_UsingReferenceCount;
                lock (this.m_References)
                {
                    if (this.m_References.Count > 0)
                        return (T)this.m_References.Dequeue();
                }
                ++this.m_AddReferenceCount;
                return Activator.CreateInstance(m_ReferenceType) as T;
            }
            
            public IReference Acquire()
            {
                ++this.m_AccquireReferenceCount;
                ++this.m_AddReferenceCount;
                lock (this.m_References)
                {
                    if(this.m_References.Count > 0)
                        return this.m_References.Dequeue();
                }

                ++this.m_AddReferenceCount;
                return (IReference)Activator.CreateInstance(m_ReferenceType);
            }
            
            /// <summary>
            /// 回收引用
            /// </summary>
            /// <param name="reference"></param>
            /// <exception cref="Exception"></exception>
            public void Release(IReference reference)
            { 
                reference.Clear();
                if(reference.GetType() != this.m_ReferenceType)
                    throw new Exception($"Type is Invalid");
                lock (this.m_References)
                {
                    if (this.m_References.Contains(reference))
                        throw new Exception("The reference has been released!");
                    m_References.Enqueue(reference);
                }
                
                ++this.m_ReleaseReferenceCount;
                --this.m_UsingReferenceCount;
            }
            
            /// <summary>
            /// 扩充对象池
            /// </summary>
            /// <param name="count"></param>
            /// <typeparam name="T"></typeparam>
            public void Add<T>(int count) where T : class, IReference, new()
            {
                lock (this.m_References)
                {
                    this.m_AddReferenceCount += count;
                    while(count-- > 0)
                        this.m_References.Enqueue(new T());
                }
            }
            
            /// <summary>
            /// 缩减对象池
            /// </summary>
            /// <param name="count"></param>
            public void Remove(int count)
            {
                lock (this.m_References)
                {
                    if(count > this.m_References.Count)
                        count -= this.m_References.Count;
                    this.m_RemoveReferenceCount += count;
                    while (count-- > 0)
                        this.m_References.Dequeue();
                }
            }

            /// <summary>
            /// 清空对象池
            /// </summary>
            public void RemoveAll()
            {
                lock (this.m_References)
                {
                    this.m_RemoveReferenceCount += this.m_References.Count;
                    this.m_References.Clear();
                }
            }

        }
    }
}